﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dfo.Controlling;
using System.IO;

namespace Dfo.ControlPanel
{
	static class SettingsExporter
	{
		/// <summary>
		/// Creates a .bat script the user can use to start the game easily, using the options set in
		/// <paramref name="settings"/>.
		/// </summary>
		/// <param name="settings">Contains the parameters to use.</param>
		/// <param name="exportPath">The path of the .bat file to create.</param>
		/// <exception cref="System.ArgumentNullException"><paramref name="settings"/> or
		/// <paramref name="exportPath"/> is null, or username and password are not set.</exception>
		/// <exception cref="System.IO.IOException">The file could not be opened or there was an error while
		/// writing to it.</exception>
		public static void ExportToBat( StartupSettings settings, string exportPath )
		{
			settings.ThrowIfNull( "settings" );
			exportPath.ThrowIfNull( "exportPath" );
			settings.Username.ThrowIfNull( "settings.Username" );
			settings.Password.ThrowIfNull( "settings.Password" );

			StringBuilder batContent = new StringBuilder();
			batContent.AppendLine( "@echo off" );
			batContent.AppendLine( "REM Make sure you understand the risks associated with storing your password in a file on your computer." );
			batContent.AppendLine( string.Format( "REM Generated by {0} {1}", VersionInfo.AssemblyTitle, VersionInfo.AssemblyVersion ) );
			batContent.AppendLine();

			string exeName = Path.GetFileName( System.Reflection.Assembly.GetExecutingAssembly().Location );
			string exeFullPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
			string args = GetArgs( settings );

			// Try DFOCP.exe in the current directory first. If it doesn't exist (the user didn't save the .bat
			// to the DFOCP directory [reasonable considering applications are not really supposed to save stuff
			// to their program directory]), use the full path to DFOCP.exe.
			//
			// This allows the user to move the DFOCP directory if they saved the .bat in it.
			// If they did not save the .bat to the DFOCP directory and they moved the DFOCP directory, a
			// reasonably intelligent user would realize that there's no way the script could know about the moving.

			batContent.AppendLine( string.Format( "IF NOT EXIST \"{0}\" (goto :fullpath)", BatEscapeInQuotes( exeName ) ) );
			batContent.AppendLine( string.Format( "\"{0}\" {1}", BatEscapeInQuotes( exeName ), args ) );
			batContent.AppendLine( "IF ERRORLEVEL 1 (goto :error) ELSE (goto :success)" );
			batContent.AppendLine( ":fullpath" );
			batContent.AppendLine( string.Format( "\"{0}\" {1}", BatEscapeInQuotes( exeFullPath ), args ) );
			batContent.AppendLine( "IF ERRORLEVEL 1 (goto :error) ELSE (goto :success)" );
			batContent.AppendLine( ":error" );
			batContent.AppendLine( "pause" );
			batContent.AppendLine( ":success" );

			try
			{
				File.WriteAllText( exportPath, batContent.ToString() );
			}
			catch ( Exception ex )
			{
				if ( ex is ArgumentException || ex is IOException || ex is UnauthorizedAccessException
				 || ex is NotSupportedException || ex is System.Security.SecurityException )
				{
					throw new IOException( string.Format( "Could not save to {0}. {1}", exportPath, ex.Message ), ex );
				}
				else
				{
					throw;
				}
			}
		}

		private static string GetArgs( StartupSettings settings )
		{
			StringBuilder args = new StringBuilder();
			args.Append( string.Format( "-cli \"--username={0}\" \"--password={1}\"",
				BatEscapeInQuotes( settings.Username ), BatEscapeInQuotes( settings.Password ) ) );

			if ( settings.ClosePopup.HasValue )
			{
				if ( settings.ClosePopup.Value )
				{
					args.Append( " -closepopup" );
				}
				else
				{
					args.Append( " -noclosepopup" );
				}
			}

			if ( settings.LaunchWindowed.HasValue )
			{
				if ( settings.LaunchWindowed.Value )
				{
					args.Append( " -windowed" );
					if ( settings.GameWindowWidth.HasValue )
					{
						args.AppendFormat( " --width={0}", settings.GameWindowWidth.Value.ToString() );
					}
					if ( settings.GameWindowHeight.HasValue )
					{
						args.AppendFormat( " --height={0}", settings.GameWindowHeight.Value.ToString() );
					}
				}
				else
				{
					args.Append( " -full" );
				}
			}

			if ( settings.DfoDir != null )
			{
				args.Append( string.Format( " \"--dfodir={0}\"", BatEscapeInQuotes( settings.DfoDir ) ) );
			}

			foreach ( ISwitchableFile switchableFile in settings.SwitchableFiles.Values )
			{
				if ( settings.SwitchFile[ switchableFile.Name ].HasValue )
				{
					if ( settings.SwitchFile[ switchableFile.Name ].Value )
					{
						args.Append( string.Format( " -{0}", switchableFile.WhetherToSwitchArg ) );
					}
					else
					{
						args.Append( string.Format( " -no{0}", switchableFile.WhetherToSwitchArg ) );
					}
				}

				if ( switchableFile.CustomFile != null )
				{
					args.Append( string.Format( " \"--{0}={1}\"",
						switchableFile.CustomFileArg, BatEscapeInQuotes( switchableFile.CustomFile ) ) );
				}

				if ( switchableFile.TempFile != null )
				{
					args.Append( string.Format( " \"--{0}={1}\"",
						switchableFile.TempFileArg, BatEscapeInQuotes( switchableFile.TempFile ) ) );
				}
			}

			return args.ToString();
		}

		private static string BatEscapeInQuotes( string stringToEscape )
		{
			return stringToEscape.Replace( "\"", "\"\"" ); // Replace " with "" - embedded quotes should be two quotes to get properly passed
			// I am not 100% sure this is all that needs to be done.
		}
	}
}
